|
Implementing Single Transactions
|
|
As previously mentioned, the simplest GPIF waveforms to produce are the single read and single
write waveforms. Performing this
function first not only enhances the initial user experience with the design, but also allows the design to be in fairly good shape, fairly quickly. Of
course, for high bandwidth applications the natural migration is then to create FIFO read and write transactions.
But, the implementation of single
transactions is the right place to start. The
first section discusses the single transaction waveforms implemented using GPIF Designer and the next section
covers the firmware that triggers them.
Figure
12. GPIF Designer Block Diagram
The GPIF Designer makes creating GPIF waveform
descriptors easy. Rather than having to know each bit of the waveform
descriptor opcode bytes in detail to create a waveform, the GPIF
Designer allows you to “draw” each waveform and export the waveform
descriptors to a self-contained file, typically called gpif.c. When
you open GPIF Designer, it will present you with a block diagram
view of the physical interconnect as shown in Figure 12. You can
then use the block diagram view to name the individual
CTLx lines and
RDYn signals. These names propagate into the waveform tabs, allowing
you to personalize each waveform and determine which signals are
being manipulated. You can also use the block diagram to configure
the clock properties of IFCLK, select different package types, and
label the external slave device.
We
can see that the naming conventions are consistent with the hardware
set-up. The single write waveform (waveform 1) is shown below in
Figure 13.
|
|
 Figure
13. Single Write waveform in GPIF Designer
|
|
For the single write
waveform, data is written to the external FIFO in S0 by making CTL0
a logic LOW (WEN is asserted) and placing data on the bus (Activate
Data) for one IFCLK cycle (Wait 1). At 48 MHz, one IFCLK cycle is
20.83 ns. With the IFCLK output inverted, this provides enough set-up
and hold time for the data.
S1 is a decision-point
state that forces an unconditional branch to the IDLE state, which
terminates the waveform (no activity occurs in the IDLE state).
A decision point state allows you to pick, at most, two terms to
evaluate a logical expression. Based on the results of that evaluation,
you can control the next state the waveform goes to. See the FX2
Technical Reference Manual for more information on decision point
states. Also in S1, CTL0 is a logic HIGH (WEN is de-asserted), and
the data bus is tri-stated (De-activate Data).
Every time a single
write waveform is initiated, the GPIF engine will cycle through
S0, S1, and then IDLE (S7).
The single read waveform (waveform 0)
is very similar to the single write waveform. The single read waveform
is shown in Figure 14.
|
|
 Figure
14. Single Read waveform in GPIF Designer
For the single read
waveform, CTL1 starts off as a logic LOW in S0 (REN asserted) for
one IFCLK cycle. This is to account for a t ENS set-up time for
the external FIFO before OE (CTL2) is asserted. S1 then asserts
OE, and in S2 the data bus is sampled (Activate Data) and an unconditional
branch to the IDLE state is taken to terminate the waveform (no
activity occurs in the IDLE state).
Note that the data
bus is sampled in S2 when it would be tempting to sample it in S1.
At the beginning of S1, the data is not yet available from the external
FIFO, therefore the GPIF has to “catch” the data at the beginning
of S2. This is why the data bus is sampled in S2 instead of S1.
Every time a single
read waveform is initiated, the GPIF engine will cycle through S0,
S1, S2, and then IDLE (S7). Notice that waveforms 2 and 3 are unused
for the single transaction example, but will be used later for the
FIFO transaction example.
|
|
Single Transaction
Firmware
|
|
After the single transaction waveforms were implemented in the GPIF Designer, the next step was to integrate the USB portion of the
overlying firmware with the GPIF Designer output to perform write and read operations to and from the external FIFO. To do this a firmware frameworks project
was copied and the code that performed the external FIFO operations was added to the TD_Poll() function within FX2_extsyncfifo.c (note that periph.c was
renamed to something more meaningful here). Endpoint and GPIF register initialization is performed in the TD_Init() function, which is also within
FX2_extsyncfifo.c.
When the user opens up the Keil uVision2 project for the FIFO example, the following should be the list of files shown in the Project Window: |

|
|
|
Ezusb.lib Collection
of functions that handle suspend, resume, I2C operations, etc.
|
USBJmpTb.OBJ Interrupt
vector jump table for USB (INT2) and GPIF/Slave FIFO (INT4)
interrupt sources.
|
dscr.a51 Device
descriptor tables for the FIFO example which report EP2OUT and
EP6IN as the available endpoints for the FX2 device.
|
FX2_to_extsyncFIFO.c Main
user application code where TD_Poll() and TD_Init() can be found.
The user will mainly be modifying this particular file and will
not need to touch fw.c.
|
gpif.c File
that contains the GPIF waveform descriptor tables that implement
the Single/FIFO GPIF transaction waveform behavior.
|
|
|
The first task at hand was to setup the endpoints appropriately for this example. The following code switches the CPU clock
speed to 48MHz (since at power-on default it is 12MHz), and sets up EP2 as an OUT endpoint, 4x buffered of size 512, and EP6 as an IN endpoint, also
4x buffered of size 512. This setup utilizes the maximum allotted 4KB FIFO space. It also sets up the FIFOs for manual mode, word wide operation and
goes through a FIFO reset and arming sequence to ensure that they are ready for data operations.
|
// set the CPU clock to 48MHz
CPUCS = ((CPUCS & ~bmCLKSPD) | bmCLKSPD1);
SYNCDELAY;
EP2CFG = 0xA0; // EP2OUT, bulk, size 512, 4x buffered
SYNCDELAY;
EP4CFG = 0x00; // EP4 not valid
SYNCDELAY;
EP6CFG = 0xE0; // EP6IN, bulk, size 512, 4x buffered
SYNCDELAY;
EP8CFG = 0x00; // EP8 not valid
SYNCDELAY;
EP2FIFOCFG = 0x01; // manual mode, disable PKTEND zero length send, word ops
SYNCDELAY;
EP6FIFOCFG = 0x01; // manual mode, disable PKTEND zero length send, word ops
SYNCDELAY;
FIFORESET = 0x80; // set NAKALL bit to NAK all transfers from host
SYNCDELAY;
FIFORESET = 0x02; // reset EP2 FIFO
SYNCDELAY;
FIFORESET = 0x06; // reset EP6 FIFO
SYNCDELAY;
FIFORESET = 0x00; // clear NAKALL bit to resume normal operation
SYNCDELAY;
// out endpoints do not come up armed
// since EP2OUT is quad buffered we must write dummy byte counts four times
EP2BCL = 0x80; // arm EP2OUT by writing byte count w/skip.
SYNCDELAY;
EP2BCL = 0x80;
SYNCDELAY;
EP2BCL = 0x80;
SYNCDELAY;
EP2BCL = 0x80;
SYNCDELAY;
GpifInit (); // initialize GPIF registers
|
|
|
TD_Init then calls the function GPIFInit() that resides in gpif.c. GPIFInit() is where the loading of the GPIF waveform
descriptor table into on-chip memory takes place and other GPIF registers get setup. An important register, IFCONFIG, also gets setup here to define
how the physical interface operates. Table 2 goes through the reasoning behind the setup of the IFCONFIG register for this example.
Note that all these bit assignments were made, automatically
by GPIF Designer as a consequence of the Block Diagram configuration.
|
|
Bit
#
|
Bit
Label
|
Contents
/ Description
|
|
7
|
IFCLKSRC
|
Set
to 1 to run the GPIF using the internal clock source
|
|
6
|
3048MHz
|
Set to 1 to run the
internal clock source for the GPIF at 48MHz.
|
|
5
|
IFCLKOE
|
Set to 1 to turn on
the IFCLK output to drive the WCLK and RCLK inputs of
the external FIFO.
|
|
4
|
IFCLPOL
|
Set to 1 to invert
the IFCLK output to the external FIFO. This allows enough
setup time for the external FIFO.
|
|
3
|
ASYNC
|
Set to 0 to operate
the GPIF at the highest rate (sync mode).
|
|
2
|
GSTATE
|
Set to 1 to turn on
the debug outputs of the state machine. PE[2:0] displays
the states the GPIF engine cycles through during each
transaction (Note: PE[2:0] are only available on the
100- and 128-pin packages).
|
|
1
|
IFCFG1
|
Set to 1 to put the
FX2 part into GPIF mode (internal master).
|
|
0
|
IFCFG0
|
Set to 0 to put
the FX2 part into GPIF mode (internal master).
|
Table 2. IFCONFIG register bit settings for FIFO example
|
The next thing TD_Init() does is it resets the external FIFO by pulsing PA2. This ensures that the external FIFO is at a
ground-zero state before commencing data operations. The following code does the trick:
|
|
// reset the external FIFO
OEA |= 0x04; // turn on PA2 as output pin
IOA |= 0x04; // pull PA2 high initially
IOA &= 0xFB; // bring PA2 low
EZUSB_Delay (1); // keep PA2 low for ~1ms, more than enough time
IOA |= 0x04; // bring PA2 high
|
A vendor command was also setup in the DR_VendorCmnd() function so that the user could reset the external FIFO at any time by
performing a vendor request of 0xB2 from the EZ-USB Control Panel.
|
|
|
In order for the data transfers to occur across the physical interface, the CPU needs to trigger the GPIF waveforms by accessing
the registers XGPIFSGLDATH, XGPIFSGLDATLX, and XGPIFSGLDATLNOX.
In order to trigger a GPIF Single Word Write transaction, the user writes to the XGPIFSGLDATAH< and XGPIFSGLDATLX in the following manner:
XGPIFSGLDATH = <word_value> >> 8;
XGPIFSGLDATLX = <word_value> // trigger GPIF
This effectively setups the MSB and LSB of the word value to be transferred, and the sheer act of writing to the XGPIFSGLDATLX register fires
off the Single Word Write transaction. To make things a little neater to follow in TD_Poll(), the following function was defined which basically
accepts a word value as an input argument and performs the GPIF Single Word Write transaction:
// reset the external FIFO
void GPIF_SingleWordWrite( WORD gdata )
{
while( !( GPIFTRIG & 0x80 ) ) // poll GPIFTRIG.7 Done bit
{
;
}
// using registers in XDATA space
XGPIFSGLDATH = gdata;
XGPIFSGLDATLX = gdata >> 8; // trigger GPIF Single Word Write transaction
}
This function also checks to see if the GPIF is in the IDLE state (GPIFTRIG.7 is set if GPIF is IDLE) before it launches the transaction. This is
something that is necessary before launching any GPIF transaction. Note that the access to the single transaction registers is swapped here because
the endpoint buffer is organized as a FIFO. The swapping ensures that the first byte in the endpoint buffer is written out FD[7:0], and the second
byte is written out FD[15:8].
In order to trigger a GPIF Single Word Read transaction, the user performs a dummy read from the XGPIFSGLDATX register. The word value just read will
be contained in the registers XGPIFSGLDATH and XGPIFSGLDATLNOX.
To make things a little neater to follow in TD_Poll(), the following function was defined which basically accepts a word pointer for the
destination variable as an input argument and performs the GPIF Single Word Read transaction:
void GPIF_SingleWordRead( WORD xdata *gdata )
{
static BYTE g_data = 0x00; // dummy variable
while( !( GPIFTRIG & 0x80 ) ) // poll GPIFTRIG.7 Done bit
{
;
}
// using register in XDATA space
g_data = XGPIFSGLDATLX; // dummy read to trigger GPIF Single Word Read transaction
while( !( GPIFTRIG & 0x80 ) ) // poll GPIFTRIG.7 Done bit
{
;
}
// using register(s) in XDATA space, retrieve word just read from external FIFO
*gdata = ( ( WORD )XGPIFSGLDATLNOX << 8 ) | ( WORD )XGPIFSGLDATH;
}
This function first checks to see if the GPIF is IDLE and then performs a dummy read from XGPIFSGLDATLX to fire off the GPIF Single Word Read
transaction. Then, another check is performed before accessing the registers that contain the word value.
|
|
|
The TD_Poll() function is where the main application code resides. The firmware here calls the functions GPIF_SingleWordWrite() and
GPIF_SingleWordRead to send and receive data from EP2OUT and EP6IN, respectively.
Code that handles USB OUT transfers
if(!(EP2468STAT & bmEP2EMPTY) && (EXTFIFONOTFULL))
{
// if host sent data to EP2OUT AND external FIFO is not full
Tcount = (EP2BCH << 8) + EP2BCL; // load transaction count with EP2 byte count
Tcount /= 2; // divide by 2 for word wide transaction
Source = (WORD *)(&EP2FIFOBUF);
for( i = 0x0000; i < Tcount; i++ )
{
// transfer data from EP2OUT buffer to external FIFO
GPIF_SingleWordWrite (*Source);
Source++;
}
EP2BCL = 0x80; // re-arm EP2OUT
}
The first thing the OUT handling code does is it checks to see if the host sent data to EP2OUT, and if the external FIFO is not full by accessing the
GPIFREADYSTAT register (EXTFIFONOTEMPTY is a macro for GPIFREADYSTAT & bmBIT0).
If both conditions are met, the word variable Tcount is setup appropriately. Since each GPIF Single Word Write transaction sends an entire word to
the external FIFO, the number of transactions is always half the number of bytes actually contained within the endpoint buffer.
A for loop then calls the GPIF_SingleWordWrite function and indexes through the endpoint buffer values, sending a word out to the external FIFO
at a time. The last step then is to re-arm the endpoint buffer so that the next USB data packet can be accepted.
Code that handles USB IN transfers
if(in_enable) // if IN transfers are enabled
{
if(!(EP2468STAT & bmEP6FULL) && (EXTFIFONOTEMPTY))
{
// if EP6IN is not full AND there is data in the external FIFO
Destination = (WORD *)(&EP6FIFOBUF);
for( i = 0x0000; i < Tcount; i++ )
{
// transfer data from external FIFO to EP6IN buffer
GPIF_SingleWordRead (Destination);
Destination++;
}
Tcount *= 2; // multiply by 2 to obtain byte count value
EP6BCH = MSB(Tcount);
SYNCDELAY;
EP6BCL = LSB(Tcount); // arm EP6IN to send data to the host
SYNCDELAY;
}
}
Another vendor command (0xB3) is setup to enable the IN transfers to occur. Otherwise, the code will just sit there and not process the INs. The
reason for the in_enable flag is so that the user can test each read and write operation independently. Otherwise, after the OUT handling code, the
IN is processed immediately. This is also useful for debugging purposes with the logic analyzer. It allows the user to capture each read/write
operation relatively easily.
If the in_enable flag is set, the code will fall through and check if the EP6IN endpoint buffer is not full, and if the external FIFO is not empty
(The CPU can check the status of the RDY signals by accessing the GPIFREADYSTAT register, so EXTFIFONOTEMPTY is a macro for GPIFREADYSTAT & bmBIT1).
If both conditions are met, a for loop then calls the GPIF_SingleWordRead function and indexes through the endpoint buffer values, receiving a word from
the external FIFO at a time. The last step then is to re-arm the endpoint buffer so that the next USB data packet can be accepted. Since each GPIF
Single Word Read transaction receives an entire word from the external FIFO, the number of transactions is always half the number of bytes actually
contained within the endpoint buffer.
|
|
Running the example for GPIF Single Transactions |
|
Now that the user understands how this FIFO example works, the bulk loop back function can be exercised by performing the steps discussed in this section.
Step 1: Download the firmware using the EZ-USB Control Panel
a)
Unzip the "FX2_extsyncFIFO GPIF Single Transactions.zip" package in the C:\Cypress\Usb\Examples\FX2 directory.
b)
After the user plugs-in the FX2 board, launch the EZ-USB Control Panel and ensure that the selected target is FX2.
c)
Then, press the "Download" button and select the FX2_extsyncFIFO.hex file. The FX2 board renumerates as a Cypress EZ-USB Sample Device and LED0 should come up flashing.
d)
Perform a "Get Pipes" and "Get Dev" to verify one more time that the firmware is up and running. The user should then see the following screen shown below:
|
|
Step 2: Setup bulk IN transfer and send 512 bytes to the external FIFO
a)
On the same line as the "BulkTrans" button, select Endpoint 6 IN as the "Pipe" and specify a "Length" of 512 bytes. Then click the
"BulkTrans" button. This will setup a bulk IN transfer of 512 bytes to read that amount from the external FIFO. Select View -> Pending Ops to
see the pending bulk IN transfer.
b)
On the same line as the "FileTrans.." button, select Endpoint 2 OUT as the "Pipe". Press the "FileTrans.." button and select the 512_count.hex
file. Click on "Open" and this action will send out 512 bytes out to the external FIFO (the data stream is a ramp).
c)
Even though 512 bytes have been written into the external FIFO the IN transfer is not processed. This is because the in_enable flag in the firmware has not yet been set to TRUE.
|
|
Step 3: Complete IN transfer to read back 512 bytes from the external FIFO
In order to complete the pending IN transfer and read back 512 bytes from the external FIFO, the in_enable flag must be set to TRUE (remember that
this allows the INs to be processed in the TD_Poll routine). To set the flag, on the same line as the "Vend Req" button, enter a value of 0xb3 in the
"Req" field. Then click the "Vend Req" button. The user should now see the 512 bytes read back from the external FIFO displayed in the window.
|
|
The bulk loop back function can also be exercised by running the bulkloop.exe utility supplied with the EZ-USB development kit software. After
downloading the firmware, launch the bulkloop.exe utility found in the C:\Cypress\Usb\Bin sub-directory. The user should setup the parameters
according to the following screen:

Prior to clicking the "Start" button to commence the bulk loop back transfers, the user should perform the 0xb3 vendor request to set the in_enable flag
to TRUE. By clicking the "Start" button, the user should see the "Pass" counter increment as each loop back transfer is exercised. Clicking on the "Stop"
button will end the loop back transfers. The data values are also checked by the bulkloop utility on each pass, so the user should see the "Error" count
increment if any data value does not match. The application will also stop on any error if the "Stop on Error" checkbox is selected.
Debug Tip:
While running this example and at any time during GPIF development, the user is strongly encouraged to connect a logic analyzer to the relevant signals
on the development kit headers. Monitoring the GPIF bus transactions aids debug sessions tremendously, and is essential for anyone seriously interested
in writing GPIF firmware. The next topic presents the waveforms the user should see on the logic analyzer as the example is run. An HP1660C Logic
Analyzer was used to capture the waveforms.
|
|
Logic Analyzer Waveforms |
The waveform above shows the timing generated by the GPIF engine for the Single Write waveform as defined by the GPIF tool. All the essential signals
are presented here, including GSTATE[2:0], which displays the states the GPIF engine cycles through as it performs the Single Write transaction.
Debug Tip:
Bringing out the GSTATE signals to the logic analyzer headers allows the user to correlate between the waveforms generated by the GPIF tool, and
the actual waveforms generated on the physical interface. This also aids the debugging process because the user can see the immediate effect of
changing the waveform behavior in the GPIF tool.
As expected from the GPIF tool output, S0 places the data on the bus (PORTB is FD[7:0] and PORTD is FD[15:8]), and asserts CTL0 (connected to the
external FIFO's WEN/ line). This effectively writes the 16-bit data value into the external FIFO. Note here that enough data setup time to the
rising edge of IFCLK is provided, since the minimum data setup time for the external FIFO is 4 ns (see CY4265 datasheet). S1 is a decision point
state that unconditionally branches to the IDLE state to terminate the transaction. Without the unconditional branch, the GPIF engine would
sequentially move through the remaining states until the IDLE state (S7) is reached.
For every word written out in a bulk OUT transfer, the user should see the GPIF engine cycle through S0, S1, and S7. To capture the waveform, the
user should trigger the logic analyzer on the falling edge of CTL0. A sampling rate of 4 ns will give the user the same resolution shown in the
waveform above.
|
Single Read Waveform
The waveform above shows the timing generated by the GPIF engine for the Single Read waveform as defined by the GPIF tool. All the essential signals
are presented here, including GSTATE[2:0], which displays the states the GPIF engine cycles through as it performs the Single Read transaction.
As expected from the GPIF tool output, S0 asserts CTL1 (connected to the external FIFO's REN/ line), S1 asserts CTL2 (connected to the external
FIFO's OE/ line), and S2 samples the data bus (PORTB is FD[7:0] and PORTD is FD[15:8]). This effectively reads the 16-bit data value into the
external FIFO. Note here that enough data setup time to the rising edge of IFCLK is provided, since the minimum data setup time for the external
FIFO is 4 ns (see CY4265 datasheet). S2 is a decision point state that unconditionally branches to the IDLE state to terminate the transaction.
Without the unconditional branch, the GPIF engine would sequentially move through the remaining states until the IDLE state (S7) is reached.
For every word read out from the external FIFO in a bulk IN transfer, the user should see the GPIF engine cycle through S0, S1, S2, and S7. To
capture the waveform, the user should trigger the logic analyzer on the falling edge of CTL1. A sampling rate of 4 ns will give the user the same
resolution shown in the waveform above.
|
|
|
|
|
|
|
|
|
|
|
|
|